package iotCmdDict

import (
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntityManager"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisRpc"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Json"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Uuid"
	"strconv"
	"strings"
)

type iotCmdDict struct {
}

type iotCmdNbiEntity struct {
	paraKey  string      // 必填参数：在云端定义的参数key
	dataKey  string      // 必填参数：数据key
	dataType string      // 必填参数：数据类型
	paraCode interface{} // 选填参数：当dataType为string的时候，可进行编码格式的转换
	dataCode interface{} // 选填参数：当dataType为string的时候，可进行编码格式的转换
	enumPara interface{} // 选填参数：当dataType为enum的时候，可进行枚举值的转换
}

func (e *iotCmdDict) buildOperateTaskEntity(templateEntity *commEntity.IotTemplateEntity) (*commEntity.OperateManualTaskEntity, error) {
	iotName := templateEntity.IotName
	subsetName := templateEntity.SubsetName

	cloudApiParam := Map.GetMap(templateEntity.TemplateParam, "cloudApiParam", nil)
	if cloudApiParam == nil {
		return nil, errors.New("北向模板参数格式不正确：iotName=" + iotName + ",subsetName=" + subsetName)
	}

	operateParam := Map.GetMap(templateEntity.TemplateParam, "operateParam", nil)
	if cloudApiParam == nil {
		return nil, errors.New("北向模板参数格式不正确：iotName=" + iotName + ",subsetName=" + subsetName)
	}

	paraKey := Map.GetString(cloudApiParam, "paraKey", "")

	// 取出参数
	manufacturer := Map.GetString(operateParam, "manufacturer", "")
	deviceType := Map.GetString(operateParam, "deviceType", "")
	templateName := Map.GetString(operateParam, "templateName", "")
	templateType := Map.GetString(operateParam, "templateType", "")

	if Method.HasEmpty(manufacturer, deviceType, subsetName, templateName, templateType) {
		return nil, errors.New("北向模板参数格式不正确：iotName=" + iotName + ",subsetName=" + subsetName + ",paraKey=" + paraKey)
	}

	find := commEntity.Type.DeviceTemplateEntity.NewEntity().(*commEntity.DeviceTemplateEntity)
	find.SubsetName = subsetName
	find.TemplateType = templateType
	find.TemplateName = templateName
	find.Manufacturer = manufacturer
	find.DeviceType = deviceType

	val, err := commEntityManager.GetEntityByServiceKey(find.Type(), find.MakeServiceKey())
	if err != nil || val == nil {
		return nil, errors.New("北向模板参数格式不正确：iotName=" + iotName + ",subsetName=" + subsetName + ",paraKey=" + paraKey)
	}
	exist := val.(*commEntity.DeviceTemplateEntity)

	operateMode := Map.GetString(exist.TemplateParam, "operateMode", "")
	operateName := Map.GetString(exist.TemplateParam, "operateName", "")
	timeout := Map.GetInt(exist.TemplateParam, "timeout", -1)
	param := Map.GetMap(exist.TemplateParam, "param", nil)

	// 参数检查
	if Method.HasEmpty(manufacturer, deviceType, operateMode, operateName) || timeout == -1 || param == nil {
		return nil, errors.New("北向模板绑定的设备模板，参数格式不正确：iotName=" + iotName + ",subsetName=" + subsetName + ",paraKey=" + paraKey)
	}

	operateManualTaskEntity := commEntity.Type.OperateManualTaskEntity.NewEntity().(*commEntity.OperateManualTaskEntity)
	operateManualTaskEntity.Manufacturer = manufacturer
	operateManualTaskEntity.DeviceType = deviceType
	operateManualTaskEntity.TaskParam = append(operateManualTaskEntity.TaskParam, exist.TemplateParam)

	return operateManualTaskEntity, nil
}

func (e *iotCmdDict) findIotTemplateEntity(manufacturer, deviceType, iotName, subsetName, paraKey string) *commEntity.IotTemplateEntity {
	entity := commEntityManager.FindEntity(commEntity.Type.IotTemplateEntity.Type(), func(value commEntity.IEntity) bool {
		entity := value.(*commEntity.IotTemplateEntity)

		if iotName != entity.IotName {
			return false
		}
		if "operate-param" != entity.TemplateType {
			return false
		}

		cloudApiParam := Map.GetMap(entity.TemplateParam, "cloudApiParam", nil)
		if cloudApiParam == nil {
			return false
		}

		// 构造NBI参数
		nbiEntity, err := e.buildNbiEntity(cloudApiParam)
		if err != nil || nbiEntity == nil {
			return false
		}

		if paraKey != nbiEntity.paraKey {
			return false
		}

		operateParam := Map.GetMap(entity.TemplateParam, "operateParam", nil)
		if operateParam == nil {
			return false
		}

		if subsetName != operateParam["subsetName"] {
			return false
		}
		if manufacturer != operateParam["manufacturer"] {
			return false
		}
		if deviceType != operateParam["deviceType"] {
			return false
		}

		return true
	})

	if entity != nil {
		return entity.(*commEntity.IotTemplateEntity)
	}

	return nil
}

func (e *iotCmdDict) BuildTaskRequestVO(deviceEntity *commEntity.DeviceEntity, iotName, subsetName, paraKey string, value interface{}) *commRedisRpc.TaskRequestVO {
	if Method.HasEmpty(paraKey) || value == nil {
		return nil
	}

	manufacturer := deviceEntity.Manufacturer
	deviceType := deviceEntity.DeviceType

	// 查找北向模板
	templateEntity := e.findIotTemplateEntity(manufacturer, deviceType, iotName, subsetName, paraKey)
	if templateEntity == nil {
		return nil
	}

	// 从北向模板中，取出cloudApiParam参数
	cloudApiParam := Map.GetMap(templateEntity.TemplateParam, "cloudApiParam", nil)
	if cloudApiParam == nil {
		return nil
	}

	// 将cloudApiParam参数，构造成IotCmdNbiEntity
	nbiEntity, err := e.buildNbiEntity(cloudApiParam)
	if err != nil || nbiEntity == nil {
		return nil
	}

	taskEntity, err := e.buildOperateTaskEntity(templateEntity)
	if err != nil || taskEntity == nil {
		return nil
	}

	taskParam := taskEntity.TaskParam[0]

	operateMode := Map.GetString(taskParam, "operateMode", "")
	operateName := Map.GetString(taskParam, "operateName", "")
	timeout := Map.GetInt(taskParam, "timeout", -1)
	param := Map.GetMap(taskParam, "param", nil)

	// 参数检查
	if Method.HasEmpty(manufacturer, deviceType, operateMode, operateName) || timeout == -1 || param == nil {
		return nil
	}

	// 转换参数
	operateParam := e.buildOperateParam(nbiEntity, param, value)

	return e.buildTaskRequestVO(deviceEntity, operateMode, operateName, operateParam, timeout)
}

func (e *iotCmdDict) buildTaskRequestVO(deviceEntity *commEntity.DeviceEntity, operateMode, operateName string, operateParam map[string]interface{}, timeout int) *commRedisRpc.TaskRequestVO {
	// 根据模板的参数，开始构造发送给设备的批量服务请求
	taskRequestVO := commRedisRpc.Type.TaskRequestVO.New()
	taskRequestVO.ClientName = "iot-cmd"
	taskRequestVO.Uuid = Uuid.RandomUUID()

	// 操作命令
	operateRequestVO := commRedisRpc.Type.OperateRequestVO.New()
	operateRequestVO.Manufacturer = deviceEntity.Manufacturer
	operateRequestVO.DeviceType = deviceEntity.DeviceType
	operateRequestVO.DeviceName = deviceEntity.DeviceName
	operateRequestVO.Uuid = Uuid.RandomUUID()
	operateRequestVO.OperateMode = operateMode
	operateRequestVO.OperateName = operateName
	operateRequestVO.Timeout = timeout
	operateRequestVO.Record = true
	Map.Append(operateRequestVO.Param, operateParam)
	Map.Append(operateRequestVO.Param, deviceEntity.DeviceParam)

	taskRequestVO.RequestVOS = append(taskRequestVO.RequestVOS, operateRequestVO)
	taskRequestVO.Timeout = operateRequestVO.Timeout

	return taskRequestVO
}

func (e *iotCmdDict) buildOperateParam(nbi *iotCmdNbiEntity, param map[string]interface{}, value interface{}) map[string]interface{} {
	operateParam := make(map[string]interface{})
	Json.Clone(param, operateParam)

	// 对简单参数进行转换
	value, err := e.buildValue(nbi.dataType, value)
	if err != nil {
		return operateParam
	}

	// 对enum类型进行转换
	if nbi.dataType == "enum" {
		enumPara := nbi.enumPara.(map[interface{}]interface{})
		val, ok := enumPara[value]
		if ok {
			value = val
		}
	}

	operateParam[nbi.dataKey] = value
	return operateParam
}

func (e *iotCmdDict) buildNbiEntity(data map[string]interface{}) (*iotCmdNbiEntity, error) {
	paraKey := Map.GetString(data, "paraKey", "")
	dataKey := Map.GetString(data, "dataKey", "")
	dataType := Map.GetString(data, "dataType", "")
	enumPara := Map.GetMap(data, "enumPara", nil)

	// 检查：必填参数
	if Method.HasEmpty(paraKey, dataKey, dataType) {
		return nil, errors.New("参数缺失：paraKey, dataKey, dataType")
	}

	entity := &iotCmdNbiEntity{}
	entity.paraKey = paraKey
	entity.dataKey = dataKey
	entity.dataType = dataType

	if dataType == "enum" {
		enums, err := e.buildEnum(enumPara)
		if err != nil || enums == nil {
			return nil, err
		}
		entity.enumPara = enums
	}

	return entity, nil
}

func (e *iotCmdDict) buildValue(dataType string, value interface{}) (interface{}, error) {
	if "int" == strings.ToLower(dataType) || "int32" == strings.ToLower(dataType) {
		return strconv.ParseInt(String.ToString(value), 10, 32)
	}
	if "int64" == strings.ToLower(dataType) || "long" == strings.ToLower(dataType) {
		return strconv.ParseInt(String.ToString(value), 10, 64)
	}
	if "float" == strings.ToLower(dataType) || "float32" == strings.ToLower(dataType) {
		return strconv.ParseFloat(String.ToString(value), 32)
	}
	if "float64" == strings.ToLower(dataType) || "double" == strings.ToLower(dataType) {
		return strconv.ParseFloat(String.ToString(value), 64)
	}
	if "bool" == strings.ToLower(dataType) || "boolean" == strings.ToLower(dataType) {
		return strconv.ParseBool(String.ToString(value))
	}
	if "string" == strings.ToLower(dataType) {
		return String.ToString(value), nil
	}

	return value, nil
}

func (e *iotCmdDict) buildEnum(enumPara map[string]interface{}) (map[interface{}]interface{}, error) {
	keyType := Map.GetString(enumPara, "keyType", "")
	valType := Map.GetString(enumPara, "valType", "")
	enums := Map.GetMap(enumPara, "enum", nil)

	if Method.HasEmpty(keyType, valType, enums) {
		return nil, errors.New("参数不能为空：keyType, valType, enum")
	}

	result := make(map[interface{}]interface{})
	for key, value := range enums {
		keyObj, err := e.buildValue(keyType, key)
		if err != nil {
			return nil, err
		}

		valObj, err := e.buildValue(valType, value)
		if err != nil {
			return nil, err
		}

		if keyObj == nil || valObj == nil {
			return nil, err
		}

		result[keyObj] = valObj
	}

	return result, nil
}
